package com.mushuilingfeng.ddd.user.configuration;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.mushuilingfeng.ddd.framework.*;
import com.mushuilingfeng.ddd.framework.event.DefaultDomainEventPublisherSubscribe;
import com.mushuilingfeng.ddd.framework.event.IDomainEvent;
import com.mushuilingfeng.ddd.framework.event.IDomainEventListener;
import com.mushuilingfeng.ddd.framework.event.IDomainEventSubscribe;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author 沐水淋风
 * @date 2024/9/19
 */
public class DddFrameworkConfig {
    private static final String DOMAIN_PACKAGE_NAME = "com.mushuilingfeng.ddd.user.domain";
    
    @Configuration
    @Import(value = {AsSpringBeanRegistrar.class, AsDomainEventSubscribeRegistrar.class})
    public static class FrameworkInit {
        @Resource
        private GenericApplicationContext applicationContext;
        
        @PostConstruct
        public void init() {
            component();
        }
        
        private void component() {
            DDDFrameworkInit.init(new ComponentFactory() {
                @Override
                public <T> T getComponent(Class<T> clazz) {
                    return applicationContext.getBean(clazz);
                }
                
                @Override
                public <T> List<T> getComponentList(Class<T> clazz) {
                    Map<String, T> beans = applicationContext.getBeansOfType(clazz);
                    if (CollUtil.isEmpty(beans)) {
                        return Collections.emptyList();
                    }
                    return new ArrayList<>(beans.values());
                }
                
            });
        }
        
        
        @Bean
        public DefaultDomainEventPublisherSubscribe domainDefaultEventPublisherSubscribe() {
            return new DefaultDomainEventPublisherSubscribe();
        }
    }
    
    @Configuration
    public static class ExecuteAsDomainEventSubscribe {
        @Resource
        private GenericApplicationContext applicationContext;
        
        @PostConstruct
        public void subscribe() {
            IDomainEventSubscribe subscribe = applicationContext.getBean(IDomainEventSubscribe.class);
            applicationContext.getBeansWithAnnotation(com.mushuilingfeng.ddd.framework.AsDomainEventSubscribe.class).values().forEach(bean -> {
                AsDomainEventSubscribe annotation = bean.getClass().getAnnotation(AsDomainEventSubscribe.class);
                Class<? extends IDomainEvent> eventCls = annotation.eventCls();
                IDomainEventListener<? extends IDomainEvent> listener = (IDomainEventListener<? extends IDomainEvent>) bean;
                subscribe.subscribe(eventCls, listener);
            });
        }
    }
    
    /**
     * 处理@AsDomainEventSubscribe注解
     */
    public static class AsDomainEventSubscribeRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
        private ResourceLoader resourceLoader;
        private Environment environment;
        
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}
        
        @Override
        public void setEnvironment(Environment environment) {this.environment = environment;}
        
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
            scanner.setResourceLoader(this.resourceLoader);
            scanner.setEnvironment(environment);
            scanner.addIncludeFilter(new AnnotationTypeFilter(AsDomainEventSubscribe.class));
            scanner.scan(DOMAIN_PACKAGE_NAME, "com.mushuilingfeng.ddd.user.event");
        }
        
    }
    
    /**
     * 处理@AsSpringBean注解
     */
    public static class AsSpringBeanRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
        private ResourceLoader resourceLoader;
        private Environment environment;
        
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}
        
        @Override
        public void setEnvironment(Environment environment) {this.environment = environment;}
        
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
            scanner.setResourceLoader(this.resourceLoader);
            scanner.setEnvironment(environment);
            scanner.addIncludeFilter(new AnnotationTypeFilter(AsSpringBean.class));
            scanner.scan(DOMAIN_PACKAGE_NAME);
        }
        
    }
    
    /**
     * 默认的ID生成器
     */
    @Component
    public static class DefaultIDGenerator implements IDGenerator {
        
        @Override
        public Serializable nextId() {
            return IdWorker.getId();
        }
        
        @Override
        public Serializable nextId(Object param) {
            return IdWorker.getId(param);
        }
    }
    
    
}
